#include "stdafx.h"
#include "layoutDesc.h"
#include "layoutDependencyAnalyzer.h"

CLayoutGroupDependencyAnalyzer::~CLayoutGroupDependencyAnalyzer()
{
	for ( auto* ptr : m_groups )
		delete ptr;
}

bool CLayoutGroupDependencyAnalyzer::CreateOptimalGroupOrder( const CLayoutDefinition& layout, std::vector< const CLayoutGroup* >& outGroups )	
{
	// cleanup
	for ( auto* ptr : m_groups )
		delete ptr;

	// add groups from layout and create dependencies between them
	for ( const CLayoutGroup* group : layout.GetGroups() )
		AddGroup( group );

	// create lame dependency ordering
	const int iterationsMax = 100;
	int iterationsLeft = iterationsMax;
	while ( --iterationsLeft > 0 )
	{
		bool changed = false;

		// "max" the dependency ordering
		for ( auto* ptr : m_groups )
		{
			const auto depOrder = ptr->m_order + 1;

			for ( auto* depPtr : ptr->m_dependencies )
			{
				if ( depOrder > depPtr->m_order )
				{
					depPtr->m_order = depOrder;
					changed = true;
				}
			}
		}

		// nothing changed
		if ( !changed )
			break;
	}

	// error
	if ( iterationsLeft == 0 )
	{
		int numCircularGroups = 0;
		for ( auto* ptr : m_groups )
		{
			if ( ptr->m_order >= iterationsMax-1 )
			{
				for ( auto* depPtr : ptr->m_dependencies )
				{
					if ( depPtr->m_order >= ptr->m_order )
					{
						numCircularGroups += 1;
					}
				}
			}
		}

		fprintf( stderr, "Found %d circular group dependencies:\n", numCircularGroups );
		for ( auto* ptr : m_groups )
		{
			if ( ptr->m_order >= iterationsMax-1 )
			{
				for ( auto* depPtr : ptr->m_dependencies )
				{
					if ( depPtr->m_order >= ptr->m_order )
					{
						if ( depPtr == ptr )
						{
							fprintf( stderr, "Group '%s' (self dependency)\n", 
								ptr->m_group->GetName().c_str() );
						}
						else
						{
							fprintf( stderr, "Group '%s' and group '%s'\n", 
								ptr->m_group->GetName().c_str(), depPtr->m_group->GetName().c_str() );
						}
					}
				}
			}
		}

		return false;
	}

	// sort by REVERSE order
	std::sort( m_groups.begin(), m_groups.end(), &GroupDependencies::SortByDepth );

	// extract
	outGroups.reserve( m_groups.size() );
	for ( auto* ptr : m_groups )
		outGroups.push_back( ptr->m_group );

	// layout is valid
	return true;
}

CLayoutGroupDependencyAnalyzer::GroupDependencies* CLayoutGroupDependencyAnalyzer::GetWrapper( const CLayoutGroup* group )
{
	for ( auto* ptr : m_groups )
		if ( ptr->m_group == group )
			return ptr;

	auto* newPtr = new GroupDependencies( group );
	m_groups.push_back( newPtr );
	return newPtr;
}

void CLayoutGroupDependencyAnalyzer::AddGroup( const CLayoutGroup* group )
{
	auto* wrapper = GetWrapper( group );

	// base group is always hard dependency (base group will have to appear first in the header)
	if ( group->GetBaseGroup() )
		wrapper->AddDependency( GetWrapper( group->GetBaseGroup() ) );

	// add other dependencies from elements
	// right now the only hard dependency are structures used directly, the arrays are safe
	for ( size_t i=0; i<group->GetNumElements(); ++i )
	{
		const auto* elem = group->GetElement(i);
		if ( elem->GetType() == eLayoutVar_Group )
		{
			// it's a directly used structure
			const auto* subgroup = elem->GetSubGroup();
			if ( !subgroup->IsMessage() )
			{
				wrapper->AddDependency( GetWrapper( elem->GetSubGroup() ) );
			}
		}
	}
}


